home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / ab20 / ab20_archive / utilities / printer / pf_deskjet.lzh / PF / Source / print.c < prev    next >
C/C++ Source or Header  |  1991-09-27  |  18KB  |  690 lines

  1. /*---------------------------------------------------------*
  2.  | Author:  Maurizio Loreti, aka MLO or I3NOO.             |
  3.  | Address: University of Padova - Department of Physics   |
  4.  |          Via F. Marzolo, 8 - 35131 PADOVA - Italy       |
  5.  | Phone:   (39)(49) 844-313         FAX: (39)(49) 844-245 |
  6.  | E-Mail:  LORETI at IPDINFN (BITNET); or VAXFPD::LORETI  |
  7.  |         (DECnet) - VAXFPD is node 38.257 i.e. 39169; or |
  8.  |          LORETI@PADOVA.INFN.IT (INTERNET).              |
  9.  | Home: Via G. Donizetti 6 - 35010 CADONEGHE (PD) - Italy |
  10.  *---------------------------------------------------------*/
  11.  
  12. #include <stdio.h>                      /* Standard library */
  13. #include <string.h>
  14. #include <stdarg.h>
  15. #include <stdlib.h>
  16. #include <ctype.h>
  17. #include <exec/types.h>                 /* Amiga specific */
  18. #include <intuition/intuition.h>
  19. #include <graphics/gfxbase.h>
  20. #include <libraries/dos.h>
  21. #include <libraries/reqbase.h>
  22. #include <devices/printer.h>
  23. #include <proto/exec.h>
  24. #include <proto/intuition.h>
  25. #include <proto/graphics.h>
  26. #include "mlo.h"                        /* Program specific */
  27. #include "pf2.h"
  28. #include "ext.h"
  29.  
  30. static Boolean CheckBreak(void);        /* Local functions */
  31. static void    CheckDefaults(void);
  32. static void    ClearPageBuffer(void);
  33. static void    ClearThisPage(void);
  34. static void    Detab(char *buffer, int length);
  35. static void    DoubleLine(char *left, char *right);
  36. static void    EjectPage(void);
  37. static void    Header(int type);
  38. static void    InterLine(void);
  39. static void    OutLine(void);
  40. static void    ResetPrinter(void);
  41. static void    SendBuffer(char *buffer);
  42. static void    SendToPrinter(char *fmt, ...);
  43. static void    Syntax(void);
  44.  
  45. /*-----------------------------------------*
  46.  | Routines (sorted in alphabetical order) |
  47.  *-----------------------------------------*/
  48.  
  49. static Boolean CheckBreak(void)
  50. {
  51.  
  52. /**
  53.  | Check for CTRL-C
  54. **/
  55.  
  56.   if (SetSignal(0L, SIGBREAKF_CTRL_C)  &  SIGBREAKF_CTRL_C) {
  57.     return True;
  58.   }
  59.   return False;
  60. }
  61.  
  62. static void CheckDefaults(void)
  63. {
  64.  
  65. /**
  66.  | As the name says, this procedure checks for incompatible options
  67.  | selected: e.g. the only Landscape fonts are Courier, so to give
  68.  | the command PF2 -TL ... is an error. I suppose that the internal
  69.  | fonts only are present; otherwise this procedure should be modified.
  70.  | Checks:
  71.  | -> Letter-Gothic: 12 or 24 cpi; portrait only.
  72.  | -> Times: proportional; portrait only.
  73.  | -> Courier: 10 or 20 cpi; plus 16.67 cpi for Courier Roman (not
  74.  |    allowed for Courier Italic).
  75.  | -> Landscape/Italic not allowed.
  76.  | -> The number of extra spaces inserted before every line must be
  77.  |    not negative.
  78. **/
  79.  
  80.   switch (Font) {
  81.     case GOTHIC:
  82.       Orientation = PORTRAIT;
  83.       if (Pitch == P10CPI) {
  84.         Pitch = P12CPI;
  85.       } else {
  86.         Pitch = P24CPI;
  87.       }
  88.       break;
  89.     case TIMES:
  90.       Orientation = PORTRAIT;
  91.       Pitch = PROPORTIONAL;
  92.       break;
  93.     case COURIER:
  94.       if (Pitch == P16_67CPI   &&   Style == ITALIC) {
  95.         Pitch = P20CPI;
  96.       }
  97.       break;
  98.   }
  99.  
  100.   if (Orientation == LANDSCAPE) {
  101.     Style = ROMAN;
  102.   }
  103.  
  104.   if (nBlanks < 0) {
  105.     nBlanks = 0;
  106.   } else {
  107.     if (nBlanks) {
  108.       Buffer = inBuffer + nBlanks;
  109.       memset(inBuffer, BLANK, nBlanks);
  110.     }
  111.   }
  112. }
  113.  
  114. void Cleanup(
  115.   int code
  116. ){
  117.  
  118. /**
  119.  | Releases all system resources (closes opened files
  120.  | and frees heap memory - if any). "code" is the
  121.  | status to be returned to the operating system.
  122. **/
  123.  
  124.   windowOff();
  125.  
  126.   if (fp  != NULL)        fclose(fp);
  127.  
  128.   if (pPB != NULL) {
  129.     if (pPB->line[0] != NULL)     free(pPB->line[0]);
  130.     free(pPB);
  131.   }
  132.  
  133.   if (PrinterOpened)      CloseDevice((struct IORequest *) IOrequest);
  134.   if (IOrequest != NULL)  DeleteExtIO((struct IORequest *) IOrequest);
  135.   if (printPort != NULL)  DeletePort((struct MsgPort *) printPort);
  136.  
  137.   if (!FromCLI) {
  138.     int i;
  139.     fprintf(stdout, "\nStrike <CR> to continue ... ");
  140.     while ( (i = getchar()) != '\n'   &&   i != EOF)  { }
  141.   }
  142.  
  143.   if (ReqBase != NULL) {
  144.     PurgeFiles(&fr);
  145.     CloseLibrary((struct Library *) ReqBase);
  146.   }
  147.   if (GfxBase != NULL)       CloseLibrary((struct Library *) GfxBase);
  148.   if (IntuitionBase != NULL) CloseLibrary((struct Library *) IntuitionBase);
  149.  
  150.   exit(code);
  151. }
  152.  
  153. static void ClearPageBuffer(void)
  154. {
  155.  
  156. /**
  157.  | This routine resets the content of the page buffer
  158.  | used in the 2-pages-on-a-sheet mode to all-blank
  159.  | lines, and the pointer to the next line to be filled
  160.  | to the first line in the buffer.
  161. **/
  162.  
  163.   memset(pPB->line[0], BLANK, BUFFER_SIZE);
  164.   ThisLine = 0;
  165. }
  166.  
  167. static void ClearThisPage(void)
  168. {
  169.  
  170. /**
  171.  | Called every end of file: in the normal mode, ejects a page;
  172.  | in the 2-pages mode, switches from the left to the right page
  173.  | (if there is at least a line on this page), and leaving the
  174.  | remainder of the lines on the current page all blank.
  175. **/
  176.  
  177.   switch (PageMode) {
  178.     case SINGLE_PAGE:
  179.       EjectPage();
  180.       break;
  181.     case LEFT_PAGE:
  182.       if (ThisLine) {
  183.         Header(UP);
  184.         PageMode = RIGHT_PAGE;
  185.         ThisLine = 0;
  186.       }
  187.       break;
  188.     case RIGHT_PAGE:
  189.       if (ThisLine) {
  190.         FlushBuffers();
  191.         PageMode = LEFT_PAGE;
  192.         ClearPageBuffer();
  193.       }
  194.       break;
  195.   }
  196. }
  197.  
  198. static void Detab(
  199.   char *buffer,
  200.   int length
  201. ){
  202.  
  203. /**
  204.  | Translates TAB stops to blanks: TAB stops are assumed at columns
  205.  | (1 + N * nTabs), with N = 1, 2, ... ; the default value of nTabs
  206.  | is 8, and can be changed using the command switches. In the same
  207.  | time non-printable characters are deleted from the input string:
  208.  | for non-printable I mean characters from 00 to 037 and 0177, assuming
  209.  | that characters over 0177 have special meaning different for every
  210.  | computer and that nothing can be said about them.
  211.  | The buffer can hold "length" characters only.
  212. **/
  213.  
  214.   char temp[LINE_LENGTH];                 /* Internal buffer */
  215.   char *pC1, *pC2;                        /* Temporary pointers */
  216.   char *pEnd;                             /* Pointer to the buffer end */
  217.  
  218.   strcpy(temp, buffer);
  219.   pEnd = (buffer + length - 1);
  220.   for (pC1 = temp, pC2 = buffer; *pC1 && pC2 < pEnd; pC1++) {
  221.     if (*pC1 == TAB) {
  222.       do {
  223.         *pC2++ = BLANK;
  224.       } while (((pC2 - buffer) % nTabs) && (pC2 < pEnd));
  225.     } else {
  226.       if (isspace(*pC1)  ||  !iscntrl(*pC1)) {
  227.         *pC2++ = *pC1;
  228.       }
  229.     }
  230.   }
  231.   *pC2 = NIHIL;
  232. }
  233.  
  234. void DoOutput(
  235.   char *FileName
  236. ){
  237.  
  238. /**
  239.  | Sends to the printer the given file.
  240. **/
  241.  
  242.   if ((fp = fopen(FileName, "r")) == NULL) {
  243.     printf("Can't open input file %s ...", FileName);
  244.     return;
  245.   }
  246.   printf("Printing file %s ... ", FileName);
  247.  
  248.   while (fgets(Buffer, bufferLength, fp) != NULL) {
  249.     if (CheckBreak()) {
  250.       printf("*** PF2: BREAK ***\n");
  251.       EjectPage();
  252.       ExitProgram();
  253.     }
  254.     Detab(Buffer, bufferLength);
  255.     OutLine();
  256.   }
  257.  
  258.   printf("done.\n");
  259.   fclose(fp);
  260.   fp = NULL;
  261.   ClearThisPage();
  262. }
  263.  
  264. static void DoubleLine(
  265.   char *left,
  266.   char *right
  267. ){
  268.  
  269. /**
  270.  | Prints a line in the 2-pages mode, the two parameters being
  271.  | pointers to the left-page line and the right-page line; a
  272.  | blank line is printed if the corresponding pointer is NULL.
  273. **/
  274.  
  275.   if (left == NULL) {
  276.     SendToPrinter("%c%*c%c%*c",
  277.         V_LINE, TOTAL_LENGTH, BLANK, V_LINE, SEP_LENGTH, BLANK);
  278.   } else {
  279.     SendToPrinter("%c%*c%.*s%*c%c%*c",
  280.         V_LINE, SIDE_LENGTH, BLANK, OUTPUT_LENGTH, left,
  281.         SIDE_LENGTH, BLANK, V_LINE, SEP_LENGTH, BLANK);
  282.   }
  283.  
  284.   if (right == NULL) {
  285.     SendToPrinter("%c%*c%c\r",
  286.         V_LINE, TOTAL_LENGTH, BLANK, V_LINE);
  287.   } else {
  288.     SendToPrinter("%c%*c%.*s%*c%c\r",
  289.         V_LINE, SIDE_LENGTH, BLANK, OUTPUT_LENGTH, right,
  290.         SIDE_LENGTH, BLANK, V_LINE);
  291.   }
  292. }
  293.  
  294. static void EjectPage(void)
  295. {
  296.   SendToPrinter("%c", FORM_FEED);
  297. }
  298.  
  299. void ExitProgram(void)
  300. {
  301.   ResetPrinter();
  302.   printf("Good Bye!\n");
  303.   Cleanup(SYS_NORMAL_CODE);
  304. }
  305.  
  306. void FlushBuffers(void)
  307. {
  308.  
  309. /**
  310.  | In the 2-pages mode, prints the current page as
  311.  | it is (non filled lines will be left blank).
  312. **/
  313.  
  314.   int i;
  315.  
  316.   switch (PageMode) {
  317.     case LEFT_PAGE:
  318.       if (!ThisLine) return;
  319.       for (i=0; i<ThisLine; i++) {
  320.         DoubleLine(pPB->line[i], NULL);
  321.         InterLine();
  322.       }
  323.       for (i=ThisLine; i<PAGE_LENGTH; i++) {
  324.         DoubleLine(NULL, NULL);
  325.         InterLine();
  326.       }
  327.       Header(DOWN);
  328.       break;
  329.     case RIGHT_PAGE:
  330.       for (i=ThisLine; i<PAGE_LENGTH; i++) {
  331.         DoubleLine(pPB->line[i], NULL);
  332.         InterLine();
  333.       }
  334.       Header(DOWN);
  335.       break;
  336.   }
  337. }
  338.  
  339. static void Header(
  340.   int type
  341. ){
  342.  
  343. /**
  344.  | Prints the top or the down header in the 2-pages mode:
  345.  | a solid line separated from the text by a blank line.
  346. **/
  347.  
  348.   memset(Buffer, H_LINE, TOTAL_LENGTH);
  349.  
  350.   if (type == UP) {
  351.     SendToPrinter("%c%.*s%c%*c%c%.*s%c\r",
  352.          NW, TOTAL_LENGTH, Buffer, NE, SEP_LENGTH,
  353.          BLANK, NW, TOTAL_LENGTH, Buffer, NE);
  354.     InterLine();
  355.   }
  356.   DoubleLine(NULL, NULL);
  357.   InterLine();
  358.   if (type == DOWN) {
  359.     SendToPrinter("%c%.*s%c%*c%c%.*s%c\r",
  360.          SW, TOTAL_LENGTH, Buffer, SE, SEP_LENGTH,
  361.          BLANK, SW, TOTAL_LENGTH, Buffer, SE);
  362.     EjectPage();
  363.   }
  364. }
  365.  
  366. void InitPrinter(void)
  367. {
  368.  
  369.   char *PitchID[] = {"10", "12", "16.67", "20", "24"};
  370.   UBYTE status[2] = {0, 0};
  371.   int dummy;
  372.  
  373. /**
  374.  | Connect properly to printer device
  375. **/
  376.  
  377.   printPort = (PrintIO *) CreatePort(PORT_NAME, 0);
  378.   IOrequest = (PrintIO *) CreateExtIO((struct MsgPort *) printPort,
  379.                                       sizeof(PrintIO));
  380.   if (!(PrinterOpened = (OpenDevice("printer.device", 0,
  381.                           (struct IORequest *) IOrequest, 0) == 0))) {
  382.     Cleanup(SYS_ABORT_CODE);
  383.   }
  384.  
  385. /**
  386.  | Check if the printer is there... Actually, if the printer is connected
  387.  | to the serial port, I don't know what to do: in this case I continue,
  388.  | knowing that a System requester will come after a while. If the printer
  389.  | is connected to the parallel port, printer selected (bit 0), paper out
  390.  | (bit 1) and printer offline (bit 2) are checked.
  391. **/
  392.  
  393.   FOREVER {
  394.     IOrequest->ios.io_Command = PRD_QUERY;
  395.     IOrequest->ios.io_Data    = (APTR) status;
  396.     IOrequest->ios.io_Flags   = 0;
  397.     if (DoIO((struct IORequest *) IOrequest)) {
  398.       Cleanup(SYS_ABORT_CODE);
  399.     }
  400.     if (IOrequest->ios.io_Actual == 2)  break;
  401.     if ((status[0] & 0x7) == 0x4)       break;
  402.  
  403.     puts("Please, check if your printer is ready!");
  404.     printf("When done, enter <CR> ... ");
  405.     while (((dummy = getc(stdin)) != NEWLINE) && (dummy != EOF) ) {}
  406.     puts("");
  407.   }
  408.  
  409. /**
  410.  | Printer initialisation: see the HP DeskJet 500
  411.  | user's manual for explanation...
  412. **/
  413.  
  414.   SendToPrinter("%c&l%do%dD%c(10U%c(s0u", ESC, Orientation, Lpi, ESC, ESC);
  415.   if (Pitch == PROPORTIONAL) {
  416.     SendToPrinter("1p");
  417.   } else {
  418.     SendToPrinter("0p%sh", PitchID[Pitch]);
  419.   }
  420.   SendToPrinter("%dv%ds0b%dt%dQ", Height, Style, Font, Quality);
  421. }
  422.  
  423. static void InterLine(void)
  424. {
  425.  
  426. /**
  427.  | Writing at 8 lines per inch and 6 points high characters,
  428.  | the double page border is not continuous. This procedure
  429.  | skips half line, draws the border, then skips another half
  430.  | line down to the correct placement for next printing.
  431. **/
  432.  
  433.   SendToPrinter("%c=", ESC);
  434.   DoubleLine(NULL, NULL);
  435.   SendToPrinter("%c=", ESC);
  436. }
  437.  
  438. static void OutLine(void)
  439. {
  440.  
  441. /**
  442.  | Outputs a line to the printer.
  443.  | In the normal mode, the line is sent to the port after storing a
  444.  | carriage return and a line feed after the text, and leaving to the
  445.  | printer to deal with form feeds and long lines.
  446.  | In the 2-pages mode, the line is truncated to a fixed length: if
  447.  | we are in the left page, the line is stored in the internal buffer;
  448.  | otherwise, it is printed together with the corresponding left line.
  449. **/
  450.  
  451.   int length, n;
  452.   static char *EndOfLine = "\r\n";
  453.  
  454.   length = strlen(inBuffer) - 1;
  455.  
  456.   switch (PageMode) {
  457.     case SINGLE_PAGE:
  458.       memcpy(inBuffer+length, EndOfLine, 3);
  459.       SendBuffer(inBuffer);
  460.       break;
  461.     case LEFT_PAGE:
  462.       if (length > OUTPUT_LENGTH) {
  463.         length = OUTPUT_LENGTH;
  464.       }
  465.       memcpy(pPB->line[ThisLine], Buffer, length);
  466.       if (++ThisLine >= PAGE_LENGTH) {
  467.         Header(UP);
  468.         PageMode = RIGHT_PAGE;
  469.         ThisLine = 0;
  470.       }
  471.       break;
  472.     case RIGHT_PAGE:
  473.       if ((n = OUTPUT_LENGTH - length) > 0) {
  474.         memset(Buffer+length, BLANK, n);
  475.       }
  476.       DoubleLine(pPB->line[ThisLine], Buffer);
  477.       InterLine();
  478.       if (++ThisLine >= PAGE_LENGTH) {
  479.         Header(DOWN);
  480.         PageMode = LEFT_PAGE;
  481.         ClearPageBuffer();
  482.       }
  483.       break;
  484.   }
  485. }
  486.  
  487. static void ResetPrinter(void)
  488. {
  489.  
  490. /**
  491.  | Resets the printer to the default.
  492. **/
  493.  
  494.   SendToPrinter("%cE", ESC);
  495. }
  496.  
  497. static void SendBuffer(
  498.   char *buffer
  499. ){
  500.   IOrequest->ios.io_Command = PRD_RAWWRITE;
  501.   IOrequest->ios.io_Data    = (APTR) buffer;
  502.   IOrequest->ios.io_Length  = strlen(buffer);
  503.   IOrequest->ios.io_Flags   = 0;
  504.   if (DoIO((struct IORequest *) IOrequest)) {
  505.     Cleanup(SYS_ABORT_CODE);
  506.   }
  507. }
  508.  
  509. static void SendToPrinter(
  510.   char *fmt,
  511.   ...
  512. ){
  513.   static char slate[LINE_LENGTH];
  514.   va_list vl;
  515.  
  516.   va_start(vl, fmt);
  517.   vsprintf(slate, fmt, vl);
  518.   va_end(vl);
  519.   SendBuffer(slate);
  520. }
  521.  
  522. void SetSpecialMode(void)
  523. {
  524.  
  525. /**
  526.  | Sets the internal constants for the "two pages on a sheet"
  527.  | special mode.
  528. **/
  529.  
  530.   int i;
  531.  
  532.   if ((pPB = malloc(sizeof(PageBuffer))) == NULL) {
  533.     fprintf(stderr, "Can't allocate the Page Buffer ...");
  534.     Cleanup(SYS_ABORT_CODE);
  535.   }
  536.   if ((pPB->line[0] = malloc(BUFFER_SIZE)) == NULL) {
  537.     fprintf(stderr, "Can't allocate the Line Buffer ...");
  538.     Cleanup(SYS_ABORT_CODE);
  539.   }
  540.   for (i=1; i<PAGE_LENGTH; i++) {
  541.     pPB->line[i] = pPB->line[0] + (i * OUTPUT_LENGTH);
  542.   }
  543.  
  544.   Orientation = LANDSCAPE;
  545.   Font = COURIER;
  546.   Style = ROMAN;
  547.   Pitch = P16_67CPI;
  548.   Height = 6;
  549.   Lpi = 8;
  550.   nBlanks = 0;
  551.   ClearPageBuffer();
  552. }
  553.  
  554. char **Setup(
  555.   int *pArgc,
  556.   char **argv
  557. ){
  558.  
  559. /**
  560.  | We look into the command line for switches; the function value is
  561.  | argv when pointing to the first non-switch argument (i.e. the first
  562.  | file name) - if any. If the 2-page mode was requested, the procedure
  563.  | allocates from the heap memory the internal buffer for the left-page
  564.  | lines, and a service buffer filled with the pointers to the first
  565.  | character of every line in the left page; this calling malloc(),
  566.  | to make this program more portable.
  567. **/
  568.  
  569.   int i;
  570.   char c;
  571.  
  572. /**
  573.  | Error if called from the Workbench, or if called
  574.  | from the CLI but without any argument.
  575. **/
  576.  
  577.   if (*pArgc < 2) Syntax();
  578.  
  579.   while (--(*pArgc)) {
  580.     if ((*++argv)[0] == '-') {
  581.  
  582. /**
  583.  | A switch ...
  584. **/
  585.  
  586.       for (i=1; (c = (*argv)[i]); i++) {
  587.         switch (c) {
  588.           case 'l': case 'L':
  589.             Orientation = LANDSCAPE;
  590.             break;
  591.           case 'i': case 'I':
  592.             Style = ITALIC;
  593.             break;
  594.           case 'b': case 'B':
  595.             nBlanks = atoi(*argv + ++i);
  596.             goto NextSwitch;
  597.           case 'a': case 'A':
  598.             nTabs = atoi(*argv + ++i);
  599.             goto NextSwitch;
  600.           case 'g': case 'G':
  601.             Font = GOTHIC;
  602.             break;
  603.           case 't': case 'T':
  604.             Font = TIMES;
  605.             break;
  606.           case 's': case 'S':
  607.             Pitch = P16_67CPI;
  608.             break;
  609.           case 'x': case 'X':
  610.             Pitch = P20CPI;
  611.             break;
  612.           case '6':
  613.             Height = 6;
  614.             break;
  615.           case '8':
  616.             Lpi = 8;
  617.             break;
  618.           case 'd': case 'D':
  619.             Quality = DRAFT_Q;
  620.             break;
  621.           case '2':
  622.             PageMode = LEFT_PAGE;
  623.             break;
  624.           default:
  625.             Syntax();
  626.         }
  627.       }
  628.     } else {
  629.  
  630. /**
  631.  | The first file name; perform some
  632.  | intialisations, then return to the caller.
  633. **/
  634.  
  635.       if (PageMode != SINGLE_PAGE) {
  636.         SetSpecialMode();
  637.       } else {
  638.         CheckDefaults();
  639.       }
  640.       return argv;
  641.     }
  642.  
  643. NextSwitch: ;
  644.   }
  645.  
  646. /**
  647.  | Here if no file name given; initialise
  648.  | the printer, then exit without reset.
  649. **/
  650.  
  651.   CheckDefaults();
  652.   InitPrinter();
  653.   puts("Printer initialised ... Good bye!");
  654.   Cleanup(SYS_NORMAL_CODE);
  655. }
  656.  
  657. static void Syntax(void)
  658. {
  659.  
  660. /**
  661.  | A syntax error has been detected in the command
  662.  | line; a short help is output to the screen.
  663. **/
  664.  
  665.   puts("Usage:   PF2   [switches]   [file [file [file ... ] ] ]");
  666.   puts("Switches: -l : Landscape (default is Portrait);");
  667.   puts("          -i : Italic (default is Roman);");
  668.   puts("         -bN : insert N Blanks before every output line;");
  669.   puts("         -aN : tAb stops every N characters (default: 8);");
  670.   puts("          -g : Letter-Gothic font (default is Courier);");
  671.   puts("          -t : Times font (default is Courier);");
  672.   puts("          -s : Small pitch (Courier: 16.67 cpi; "
  673.        "Letter-Gothic: 24 cpi);");
  674.   puts("          -x : eXtra-small pitch (20 cpi - for Courier only);");
  675.   puts("          -8 : 8 lines per inch (default: 6 lpi);");
  676.   puts("          -6 : 6 points high font (default: 12 points);");
  677.   puts("          -d : Draft quality (default is Letter quality);");
  678.   puts("          -2 : special mode (2 pages on every sheet of paper).\n");
  679.   puts("The s/x/l switches are ignored when incompatible with selected font.");
  680.   puts("The default pitch is 10 cpi for Courier and 12 cpi for Letter-Gothic");
  681.   puts("(Times is a proportional font);   all switches different from -d and");
  682.   puts("-a will be ignored,  if -2 is selected.  The switches on the command");
  683.   puts("line can be grouped: e.g. -c -8 -s is the same as -c8s; only -bN and");
  684.   puts("-aN, if present, must be specified alone or the last in a group. The");
  685.   puts("printer is reset to its default,  at the completion  -  UNLESS if no");
  686.   puts("file names are given. This program can be interrupted with CTRL-C.\n");
  687.  
  688.   Cleanup(SYS_NORMAL_CODE);
  689. }
  690.